#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "st7789.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include "esp_system.h"

#define ST7789_CLK_PIN  2
#define ST7789_MOSI_PIN 3
#define ST7789_RST_PIN  10
#define ST7789_DC_PIN   6
#define ST7789_CS_PIN   7
#define ST7789_BL_PIN   11

spi_device_handle_t spi;

void esp32c3_spi_write(uint8_t* bytes, uint32_t len)
{
	/* the section length must be 9 bytes aligned */
	/* if the alignment is not 9-byte, the 0x00 command is inserted */
	/* subsequent data will be lost */
	uint32_t section = 9216;
	spi_transaction_t t;

	while (len > section) {
		memset(&t, 0, sizeof(t));             // Zero out the transaction
		t.length = 8 * section;               // Command is 8 bits
		t.tx_buffer = bytes;                  // The data is the cmd itself
		spi_device_polling_transmit(spi, &t); // Transmit!
		bytes += section;
		len -= section;
	}

	memset(&t, 0, sizeof(t));             // Zero out the transaction
	t.length = 8 * len;                   // Command is 8 bits
	t.tx_buffer = bytes;                  // The data is the cmd itself
	spi_device_polling_transmit(spi, &t); // Transmit!
}

void esp32c3_rst_pin(int val)
{
	gpio_set_level(ST7789_RST_PIN, val);
}

void esp32c3_bl_pin(int val)
{
	gpio_set_level(ST7789_BL_PIN, val);
}

void esp32c3_msleep(int ms)
{
	usleep(1000 * ms);
}

void board_init()
{
	gpio_reset_pin(ST7789_RST_PIN);
	gpio_set_direction(ST7789_RST_PIN, GPIO_MODE_OUTPUT);

	gpio_reset_pin(ST7789_BL_PIN);
	gpio_set_direction(ST7789_BL_PIN, GPIO_MODE_OUTPUT);

	spi_bus_config_t buscfg = {
		.miso_io_num = -1,
		.mosi_io_num = ST7789_MOSI_PIN,
		.sclk_io_num = ST7789_CLK_PIN,
		.quadwp_io_num = -1,
		.quadhd_io_num = -1,
		.max_transfer_sz = 19200,
	};

	spi_device_interface_config_t devcfg = {
		.clock_speed_hz = 40000000,    // Clock out at 20 MHz
		.mode = 3,                     // SPI mode 3
		.spics_io_num = ST7789_CS_PIN, // CS pin
		.queue_size = 7,               // We want to be able to queue 7 transactions at a time
	};

	spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO);
	spi_bus_add_device(SPI2_HOST, &devcfg, &spi);
}

void app_main(void)
{
	struct st7789 lcd;
	uint16_t* buf = NULL;
	int x = 0;
	int y = 0;
	int w = 240;
	int h = 240;

	board_init();

	/* set lcd cfg */
	lcd.cfg.fix.width = 240;
	lcd.cfg.fix.height = 240;
	lcd.cfg.fix.xoffset = 40;
	lcd.cfg.fix.yoffset = 40;
	lcd.cfg.var.orien = 0;

	/* register the operation */
	lcd.ops.msleep = esp32c3_msleep;
	lcd.ops.reset = esp32c3_rst_pin;
	lcd.ops.backlight = esp32c3_bl_pin;
	lcd.ops.write = esp32c3_spi_write;
	st7789_init(&lcd);

	printf("init, free heap size: %dKB\n", esp_get_free_heap_size() / 1024);
	buf = malloc((w * h * 2 + 7) / 8 * 9);
	// buf = malloc(w * h * 2);
	printf("frame buffer malloc, free heap size: %dKB\n", esp_get_free_heap_size() / 1024);

	while (1) {
#if 1
		/* white */
		memset(buf, 0xff, w * h * 2);
		st7789_rect_fill(&lcd, x, y, x + w - 1, y + h - 1, buf);
		// vTaskDelay(1000 / portTICK_PERIOD_MS);

		/* black */
		memset(buf, 0x00, w * h * 2);
		st7789_rect_fill(&lcd, x, y, x + w - 1, y + h - 1, buf);
		// vTaskDelay(1000 / portTICK_PERIOD_MS);
#else
		int i = 0;
		int j = 0;

		for (i = 0; i < 240; i++)
			for (j = 0; j < 240; j++)
				st7789_point_draw(&lcd, i, j, RGB565_BLUE);

		vTaskDelay(1000 / portTICK_PERIOD_MS);

		for (i = 0; i < 240; i++)
			for (j = 0; j < 240; j++)
				st7789_point_draw(&lcd, i, j, RGB565_RED);

		vTaskDelay(1000 / portTICK_PERIOD_MS);
#endif
	}
}
